/*
 * $RCSfile: SeismoPoint.java,v $
 * $Revision: 1.2 $
 * $Date: 2006/04/01 20:24:27 $
 *
 * Copyright (c) 2001-2006: University of Piraeus.
 * People involved: Asst. Prof. Yannis Theodoridis,
 * PhD student G. Marketos and researcher Dr. I.S. Kalogeras.
 */

package seismosurfer.graphics;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;

import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.proj.ProjMath;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;

/**
 * Similar to OMPoint, but provides more shapes to mark
 * mark a specific point in the map.
 *
 */
public class SeismoPoint extends OMGraphic {
    
    private static final long serialVersionUID = -5744232817751501719L;

    public final static String CIRCLE = "circle";

    public final static String RECTANGLE = "rectangle";

    public final static String TRIANGLE = "triangle";

    public final static String STAR = "star";

    public final static String CROSS = "cross";

    protected final static double COS_72 = Math.cos(ProjMath.degToRad(72));

    protected final static double SIN_72 = Math.sin(ProjMath.degToRad(72));

    protected final static double COS_36 = Math.cos(ProjMath.degToRad(36));

    protected final static double SIN_36 = Math.sin(ProjMath.degToRad(36));

    public final static int DEFAULT_RADIUS = 4;

    public final static boolean DEFAULT_ISOVAL = false;

    /**
     * The number of pixels in the radius for the point representation.
     */
    protected int radius = DEFAULT_RADIUS;

    /**
     * Horizontal window position of point, in pixels from left side of window.
     */
    protected int x = 0;

    /**
     * Vertical window position of point, in pixels from the top of the window.
     */
    protected int y = 0;

    /** Latitude of point, decimal degrees. */
    protected float lat1 = 0.0f;

    /** Longitude of point, decimal degrees. */
    protected float lon1 = 0.0f;

    // default
    protected String pointType = CIRCLE;

    /** Default constructor, waiting to be filled. */
    public SeismoPoint() {
        super();
    }

    public SeismoPoint(float lat, float lon) {
        this(lat, lon, 0, 0, DEFAULT_RADIUS);
    }

    public SeismoPoint(float lat, float lon, int radius) {
        this(lat, lon, 0, 0, radius);
    }

    /**
     * Create a SeismoPoint at a lat/lon position with a screen X/Y pixel offset,
     * with the default radius.
     */
    public SeismoPoint(float lat, float lon, int offsetx, int offsety) {
        this(lat, lon, offsetx, offsety, DEFAULT_RADIUS);
    }

    /**
     * Create an SeismoPoint at a lat/lon position with a screen X/Y pixel offset,
     * with the specified radius.
     */
    public SeismoPoint(float lat, float lon, int offsetx, int offsety,
            int radius) {
        setRenderType(RENDERTYPE_OFFSET);
        set(lat, lon, offsetx, offsety);
        this.radius = radius;
    }

    public void setGraphicsForFill(Graphics g) {
        if (g instanceof Graphics2D) {
            ((Graphics2D) g).setStroke(BASIC_STROKE);
        }
        if (isSelected()) {
            setGraphicsColor(g, getDisplayPaint());
        } else {
            setGraphicsColor(g, getFillPaint());
        }
    }

    public String getPointType() {
        return pointType;
    }

    public void setPointType(String type) {

        if (type != pointType) {

            setNeedToRegenerate(true);

            if (type.equals(CIRCLE)) {
                pointType = type;
            } else if (type.equals(RECTANGLE)) {
                pointType = type;
            } else if (type.equals(STAR)) {
                pointType = type;
            } else if (type.equals(CROSS)) {
                pointType = type;
            } else if (type.equals(TRIANGLE)) {
                pointType = type;
            } else {
                pointType = CIRCLE;
            }
        }
    }

    /** For lat/lon rendertype points, to move the point location. */
    public void set(float lat, float lon) {
        setLat(lat);
        setLon(lon);
    }

    /** For offset rendertype points, to move the point location. */
    public void set(float lat, float lon, int offsetx, int offsety) {
        setLat(lat);
        setLon(lon);
        ;
        set(offsetx, offsety);
    }

    /**
     * For screen x/y rendertype points, to move the point location. This method
     * does not call setX() and setY().
     */
    public void set(int x, int y) {
        // You have to set these directly, or you can mess up the grab
        // points by using set methods - VerticalGrabPoints and
        // HorizontalGrabPoints disable some methods. This method is
        // used to override them, for initialization purposes.
        this.x = x;
        this.y = y;
        setNeedToRegenerate(true);
    }

    /** Set the latitude of the point, in decimal degrees. */
    public void setLat(float lat) {
        this.lat1 = lat;
        setNeedToRegenerate(true);
    }

    /** Get the latitude of the point, in decimal degrees. */
    public float getLat() {
        return lat1;
    }

    /** Set the longitude of the point, in decimal degrees. */
    public void setLon(float lon) {
        this.lon1 = lon;
        setNeedToRegenerate(true);
    }

    /** Get the longitude of the point, in decimal degrees. */
    public float getLon() {
        return lon1;
    }

    /** For screen x/y rendertype points. */
    public void setX(int x) {
        this.x = x;
        setNeedToRegenerate(true);
    }

    /** For screen x/y rendertype points. */
    public int getX() {
        return x;
    }

    /** For screen x/y rendertype points. */
    public void setY(int y) {
        this.y = y;
        setNeedToRegenerate(true);
    }

    /** For screen x/y rendertype points. */
    public int getY() {
        return y;
    }

    /**
     * Set the radius of the marking rectangle. The edge size of the marking
     * rectangle will be radius * 2 + 1.
     */
    public void setRadius(int radius) {
        this.radius = radius;
        setNeedToRegenerate(true);
    }

    /**
     * Get the radius for the point.
     */
    public int getRadius() {
        return radius;
    }

    /**
     * Prepare the rectangle for rendering.
     * 
     * @param proj
     *            Projection
     * @return true if generate was successful
     */
    public boolean generate(Projection proj) {

        if (proj == null) {
            Debug.message("omgraphic", "OMPoint: null projection in generate!");
            return false;
        }

        // reset the internals
        int x1 = 0;
        int x2 = 0;
        int y1 = 0;
        int y2 = 0;

        Point p1 = proj.forward(lat1, lon1);

        switch (renderType) {
        case RENDERTYPE_XY:
            x1 = x - radius;
            y1 = y - radius;
            x2 = x + radius;
            y2 = y + radius;
            break;
        case RENDERTYPE_OFFSET:
        case RENDERTYPE_LATLON:
            if (!proj.isPlotable(lat1, lon1)) {
                setNeedToRegenerate(true); // HMMM not the best flag
                return false;
            }

            // Point p1 = proj.forward(lat1, lon1);

            x1 = p1.x + x - radius;
            y1 = p1.y + y - radius;
            x2 = p1.x + x + radius;
            y2 = p1.y + y + radius;
            break;
        case RENDERTYPE_UNKNOWN:
            System.err.println("OMPoint.generate(): invalid RenderType");
            return false;
        }

        if (pointType.equals(CIRCLE)) {
            shape = new GeneralPath(new Ellipse2D.Float((float) Math
                    .min(x2, x1), (float) Math.min(y2, y1), (float) Math.abs(x2
                    - x1), (float) Math.abs(y2 - y1)));
        } else if (pointType.equals(TRIANGLE)) {

            GeneralPath p0 = new GeneralPath();
            p0.moveTo(p1.x, p1.y - radius);
            p0.lineTo(p1.x - radius, p1.y + radius);
            p0.lineTo(p1.x + radius, p1.y + radius);
            p0.closePath();

            shape = p0;

        } else if (pointType.equals(STAR)) {

            float X_72 = radius * (float) SIN_72;
            float Y_72 = radius * (float) COS_72;
            float X_36 = radius * (float) SIN_36;
            float Y_36 = radius * (float) COS_36;

            GeneralPath p0 = new GeneralPath();
            p0.moveTo(p1.x, p1.y - radius);
            p0.lineTo(p1.x - X_36, p1.y + Y_36);
            p0.lineTo(p1.x + X_72, p1.y - Y_72);
            p0.lineTo(p1.x - X_72, p1.y - Y_72);
            p0.lineTo(p1.x + X_36, p1.y + Y_36);
            p0.closePath();

            shape = p0;
        } else if (pointType.equals(CROSS)) {
            GeneralPath p0 = new GeneralPath();
            p0.moveTo(p1.x, p1.y - radius);
            p0.lineTo(p1.x, p1.y + radius);
            p0.moveTo(p1.x - radius, p1.y);
            p0.lineTo(p1.x + radius, p1.y);

            shape = p0;
        } else if (pointType.equals(RECTANGLE)) {
            shape = createBoxShape((int) Math.min(x2, x1), (int) Math.min(y2,
                    y1), (int) Math.abs(x2 - x1), (int) Math.abs(y2 - y1));
        }

        else { // Circle
            shape = new GeneralPath(new Ellipse2D.Float((float) Math
                    .min(x2, x1), (float) Math.min(y2, y1), (float) Math.abs(x2
                    - x1), (float) Math.abs(y2 - y1)));

        }

        setNeedToRegenerate(false);
        return true;
    }

    protected boolean hasLineTypeChoice() {
        return false;
    }

}
